/*
 * 模态对话框
 *    + title[string] 标题
 *    + template[string] 自定义的内容或者模板(基于ES6的模板字符串,拼接更丰富的内容结构)
 *    + buttons[array] 自定义按钮(组)
 *      [{text:'确定',click:[callback]},...]
 *    + modal[boolean] 控制遮罩层是否显示 默认是true
 *    + drag[boolean] 是否允许拖拽 默认是true
 *    + onopen[function] 打开
 *    + onclose[function] 关闭
 * 拖拽的生命周期函数「当前操作的某个节点上，允许用户自定义处理的事情」
 *   「发布订阅」
 *    + 拖拽开始 ondragstart
 *    + 拖拽中 ondraging
 *    + 拖拽结束 ondragend
 */
import Sub from './sub'
/* 插件核心 */
function isFunction(obj) {
    return typeof obj === "function" && typeof obj.nodeType !== "number";
}
export function Modal(config) {
    this.config = config
    this.$drag_modal = null
    this.$drag_content = null
    this.startX = 0
    this.startY = 0
    this.startL = 0
    this.startT = 0
    this._MOVE = null
    this._UP = null
    //如果开启拖拽，我们需要创建三个事件池
    if (this.config.drag) {
        this.ondragstart = new Sub
        this.ondraging = new Sub
        this.ondragend = new Sub
    }
    this.init()
}
Modal.prototype = {
    version: '1.0.0',
    constructor: Modal,
    init() {
        this.create()
        // 基于事件委托实现容器中元素点击处理的相关事务：关闭按钮 && 自定义按钮
        if (this.$drag_content) {
            this.$drag_content.addEventListener('click', (ev) => {
                let target = ev.target,
                    targetTag = target.tagName,
                    targetClass = target.className;
                // 关闭按钮
                if (targetTag === 'A' && targetClass === 'drag_close') {
                    this.close()
                    return
                }
                // 自定义按钮
                if (targetTag === 'A' && targetClass === 'drag_button') {
                    let index = +target.getAttribute('index'),
                        item = this.config.buttons[index];
                    if (item && isFunction(item.click))
                        item.click.call(this, this)
                }
            })
        }
        if (this.config.drag) {
            // 开启多拽
            let $drag_head = this.$drag_content.querySelector('.drag_head')
            $drag_head.style.cursor = 'move'
            $drag_head.addEventListener('mousedown', this.down.bind(this))
        }
    },
    // 动态创建Modal的DOM结构
    create() {
        const fragment = document.createDocumentFragment()
        // 创建遮罩层
        if (this.config.modal) {
            this.$drag_modal = document.createElement('div')
            this.$drag_modal.className = 'drag_modal'
            fragment.appendChild(this.$drag_modal)
        }
        // 创建内容
        this.$drag_content = document.createElement('div')
        this.$drag_content.role = 'alertdialog'
        this.$drag_content.ariaLabel = this.config.title
        this.$drag_content.ariaLive = 'assertive'
        this.$drag_content.className = 'drag_content'
        const buttons = this.config.buttons?.map(item => `<a href="javascript:;" class="drag_button" role="button">${item.text}</a>`) ?? []
        this.$drag_content.innerHTML = `<div class="drag_head">
                ${this.config.title}
                <a href="javascript:;" class="drag_close" role="button">关闭</a>
            </div>
            <div class="drag_main" aria-live="assertive">${this.config.template}</div>
            ${buttons.length>0?`<div class="drag_foot">
            ${buttons.join('')}</div>`:``}`
        fragment.appendChild(this.$drag_content)
        // 把动态创建的元素添加到页面中
        document.body.appendChild(fragment)
        // 控制元素显示：透明度设置为1「过渡动画」
        this.$drag_content.offsetHeight; //刷新渲染队列(获取一下样式,这样上面的添加到页面和改变元素样式就分两次执行了)
        this.$drag_modal ? this.$drag_modal.style.opacity = '1' : null
        this.$drag_content.style.opacity = '1'
        // 改变盒子居中的方式
        this.$drag_content.style.left = `${(document.documentElement.clientWidth -this.$drag_content.offsetWidth) / 2}px`
        this.$drag_content.style.top = `${(document.documentElement.clientHeight -this.$drag_content.offsetHeight) / 2}px`
        this.$drag_content.style.transform = 'translate(0,0)';
        (this.$drag_content.querySelector('a.drag_button') || this.$drag_content.querySelector('a.drag_close'))?.focus()
        // 触发打开的周期函数
        this.config.onopen?.call(this, this)
    },
    // 关闭Modal（页面中移除掉）
    close() {
        if (this.$drag_modal) {
            this.$drag_modal.style.opacity = '0'
            this.$drag_modal.ontransitionend = () => {
                // 元素的过渡动画结束后
                this.$drag_modal.remove()
                this.$drag_modal = null
            };
        }
        if (this.$drag_content) {
            this.$drag_content.style.opacity = '0'
            this.$drag_content.ontransitionend = () => {
                this.$drag_content?.remove()
                this.$drag_content = null
                // 触发关闭的周期函数
                this.config.onclose?.call(this, this)
            };
        }
    },
    // 获取模板容器
    getTemplateContainer() {
        return this.$drag_content.querySelector('div.drag_main')
    },
    // 拖拽系列方法
    down(ev) {
        this.startX = ev.pageX
        this.startY = ev.pageY
        this.startL = this.$drag_content.offsetLeft
        this.startT = this.$drag_content.offsetTop
        this._MOVE = this.move.bind(this)
        this._UP = this.up.bind(this)
        document.addEventListener('mousemove', this._MOVE)
        document.addEventListener('mouseup', this._UP)
        // 通知事件池中的方法执行
        this.ondragstart.fire(this)
    },
    move(ev) {
        let curL = ev.pageX -this.startX + this.startL,
            curT = ev.pageY -this.startY + this.startT;
        // 边界判断
        let HTML = document.documentElement,
            minL = 0,
            minT = 0,
            maxL = HTML.clientWidth - this.$drag_content.offsetWidth,
            maxT = HTML.clientHeight -this.$drag_content.offsetHeight;
        curL = curL < minL ? minL : (curL > maxL ? maxL : curL)
        curT = curT < minT ? minT : (curT > maxT ? maxT : curT)
        this.$drag_content.style.left = curL + 'px'
        this.$drag_content.style.top = curT + 'px'
        // 通知事件池中的方法执行
        this.ondraging.fire(this)
    },
    up() {
        document.removeEventListener('mousemove', this._MOVE)
        document.removeEventListener('mouseup', this._UP)
        // 通知事件池中的方法执行
        this.ondragend.fire(this)
    }
};
